home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-05
/
driverss.zip
/
UBNICPC.ASM
< prev
next >
Wrap
Assembly Source File
|
1991-02-11
|
16KB
|
694 lines
version equ 1
;History:25,1 0
; Copyright, 1990, Russell Nelson
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, version 1.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
include defs.asm
;some generic information about buffer lengths, etc
XMITBUF_LEN equ 800h ;Transmit buffer length
RPAGE_LEN equ 128 ;receive page is 128 bytes
NUM_RPAGES equ 96 ;There are 96 receive pages
NUM_XBUFS equ 2 ;Number of xmit buffers
NUM_XBUFS_MASK equ 1
ub_seg segment at 0
org 10h
eaddr_rom db 6 dup(?) ;Has serial # in 3 & 4th bytes
org 2080h
tsa_msb db ? ;transmitter start address, high order 4
;bits on write, start send on read
tsa_lsb db ? ;Transmitter start address, least
;significant byte on write, clear PAV
;on read
;Control / status register (CSR)
csr db ? ;Control on write, status on read port
;Command bits
TxRdyIntEn equ 080h ;allow TxRdy interrupts
PAVIntEn equ 040h ;Allow PAV to gen. intr.
SFTInt equ 020h ;generate an interrupt reg.
TimIntEn equ 010h ;allow Time interrupt req.
;status bits
NTxRdyInt equ 080h ;Not req. TxRdy inter.
NPAVInt equ 040h ;Not req. Pav intr.
NSFTInt equ 020h ;Not req. SFTInt inter.
NTimInt equ 010h ;Not req. timer inter.
NRINT equ 008h ;Not req. recv inter.
NTINT equ 004h ;Not req. xmit inter.
TPOK equ 002h ;Packet okay of some type
TXDONE equ 001h ;Transmit done
;Page pointer register definitions
ffp label byte ;ffp - first free page register
epp label byte ;epp - empty page pointer register
pp db ? ;ffp on write/epp on read
;ffp bits
NIC_IE equ 080h ;NIC interrupts enable
;epp bits
PAV equ 080h ;Page available
RPAGE_BITS equ 07fh
org 2100h
page_ram db NUM_RPAGES dup(?);Receiver buffer pages
org 2180h
tstatus db ? ;Transmitter status 80
;Transmit status bits - EDLC
TX_READY equ 080h ;
TX_BUSY equ 040h ;Carrier detect
TX_TPR equ 020h ;Self received packet sent
TX_SHORTED equ 010h ;Carrier lost
TX_UF equ 008h ;Underflow
TX_COL equ 004h ;Collision
TX_C16 equ 002h ;16 collisions
TX_PE equ 001h ;Parity error
TXERRMASK equ TX_PE or TX_C16 or TX_COL or TX_UF or TX_SHORTED or TX_BUSY
tmask db ? ;Transmitter mask 81
rstatus db ? ;Receiver status
rmask db ? ;Receiver mask 83
tmode db ? ;Transmitter mode 84
rmode db ? ;Receiver mode 85
reset db ? ;Reset line - high on bit 7, 86
tdrlsb db ? ;TDR lsb 87
eaddr_ram db 6 dup(?) ;current Ethernet address.
db ? ;Reserved 8e
tdrmsb db ? ;TDR msb 8f
org 4000h
receive_buf label byte
org 7000h
transmit_buf_1 label byte
org 7800h
transmit_buf_2 label byte
ub_seg ends
code segment word public
assume cs:code, ds:code
public int_no
int_no db 3,0,0,0 ;must be four bytes long for get_number.
base_addr dw 0d000h,0
public driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class db BLUEBOOK, IEEE8023, 0 ;from the packet spec
driver_type db 8 ;from the packet spec
driver_name db 'PC-NIC',0 ;name of the driver.
driver_function db 2
parameter_list label byte
db 1 ;major rev of packet driver
db 9 ;minor rev of packet driver
db 14 ;length of parameter list
db EADDR_LEN ;length of MAC-layer address
dw GIANT ;MTU, including MAC headers
dw MAX_MULTICAST * EADDR_LEN ;buffer size of multicast addrs
dw 0 ;(# of back-to-back MTU rcvs) - 1
dw 0 ;(# of successive xmits) - 1
int_num dw 0 ;Interrupt # to hook for post-EOI
;processing, 0 == none,
public rcv_modes
rcv_modes dw 7 ;number of receive modes in our table.
dw 0 ;There is no mode zero
dw rcv_mode_1
dw 0 ;don't want to bother.
dw rcv_mode_3
dw 0 ;haven't set up perfect filtering yet.
dw rcv_mode_5
dw rcv_mode_6
current_page db 0 ;current receiver page
active_buffer dw transmit_buf_1
inactive_buffer dw transmit_buf_2
include movemem.asm
public as_send_pkt
; The Asynchronous Transmit Packet routine.
; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
; interrupts possibly enabled.
; Exit with nc if ok, or else cy if error, dh set to error number.
; es:di and interrupt enable flag preserved on exit.
as_send_pkt:
ret
public drop_pkt
; Drop a packet from the queue.
; Enter with es:di -> iocb.
drop_pkt:
assume ds:nothing
ret
public xmit
; Process a transmit interrupt with the least possible latency to achieve
; back-to-back packet transmissions.
; May only use ax and dx.
xmit:
assume ds:nothing
ret
public send_pkt
send_pkt:
;enter with es:di->upcall routine, (0:0) if no upcall is desired.
; (only if the high-performance bit is set in driver_function)
;enter with ds:si -> packet, cx = packet length.
;if we're a high-performance driver, es:di -> upcall.
;exit with nc if ok, or else cy if error, dh set to error number.
assume ds:nothing
mov es,base_addr
assume es:ub_seg
cmp cx,GIANT ; Is this packet too large?
ja send_pkt_toobig
cmp cx,RUNT ; minimum length for Ether
jnb oklen
mov cx,RUNT ; make sure size at least RUNT
oklen:
mov di,XMITBUF_LEN ;compute where we're going to
sub di,cx ; be putting the packet.
;games above ensure that the last byte of data to go out is at the
;end of the ub xmit buffer, pos is really the offset from the
;start of that buffer
mov ax,active_buffer ;swap the buffer pointers.
xchg ax,inactive_buffer
mov active_buffer,ax
add di,ax
push di
call movemem
pop di
;Wait for previous packet to finish first
;could do this with interrupts, etc, but with only 2 xmit buffers
;doesn't seem worth the effort
send_pkt_1:
mov al,csr
and al,TPOK or TXDONE
cmp al,TPOK or TXDONE
jne send_pkt_1
mov al,tstatus ;get xmiter status ...
mov tstatus,0fh ;... and clear status bits
test al,TXERRMASK ;any errors?
je send_pkt_2
call count_out_err ;yes, count them.
send_pkt_2:
;all done above, we can play with TSA now!
;give offset
mov ax,di
mov tsa_lsb,al
mov tsa_msb,ah
cmp al,tsa_msb ;Start the send in action (SIDEEFFECT)
clc
ret
send_pkt_toobig:
mov dh,NO_SPACE
stc
ret
public get_address
get_address:
;get the address of the interface.
;enter with es:di -> place to get the address, cx = size of address buffer.
;exit with nc, cx = actual size of address, or cy if buffer not big enough.
assume ds:code
cmp cx,EADDR_LEN ;make sure that we have enough room.
jb get_address_2
push ds
mov ds,base_addr
mov si,offset eaddr_rom
mov cx,EADDR_LEN
rep movsb
pop ds
mov cx,EADDR_LEN
clc
ret
get_address_2:
stc
ret
public set_address
set_address:
;enter with ds:si -> Ethernet address, CX = length of address.
;exit with nc if okay, or cy, dh=error if any errors.
assume ds:nothing
cmp cx,EADDR_LEN ;ensure that their address is okay.
je set_address_4
mov dh,BAD_ADDRESS
stc
jmp short set_address_done
set_address_4:
mov es,base_addr
mov di,offset eaddr_ram
rep movsb
; call initialize ;initialize with our new address.
set_address_okay:
mov cx,EADDR_LEN ;return their address length.
clc
set_address_done:
push cs
pop ds
assume ds:code
ret
; 0 - accept no packets
rcv_mode_1:
mov es,base_addr
assume es:ub_seg
mov rmode,0
ret
rcv_mode_3:
; 1 - accept node id packets, multicasts which match first three
; bytes of node id, and broadcasts
mov es,base_addr
assume es:ub_seg
mov rmode,1
ret
; 2 - accept node id packets, all multicasts and broadcasts
rcv_mode_5:
mov es,base_addr
assume es:ub_seg
mov rmode,2
ret
; 3 - take all, promiscous
rcv_mode_6:
mov es,base_addr
assume es:ub_seg
mov rmode,3
ret
public set_multicast_list
set_multicast_list:
;enter with ds:si ->list of multicast addresses, cx = number of addresses.
;return nc if we set all of them, or cy,dh=error if we didn't.
mov dh,NO_MULTICAST
stc
ret
public terminate
terminate:
mov es,base_addr
assume es:ub_seg
mov ffp,0 ;set ffp to zero - disabling PAV
mov csr,0 ;Turn off interrupt masks here and ...
mov tmask,0 ;reset edlc interrupt masks
mov rmask,0
mov reset,80h ;Turn on reset line
ret
public reset_interface
reset_interface:
;reset the interface.
assume ds:code
ret
;called when we want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type, dl = packet class.
extrn recv_find: near
;called after we have copied the packet into the buffer.
;enter with ds:si ->the packet, cx = length of the packet.
extrn recv_copy: near
extrn count_in_err: near
extrn count_out_err: near
public recv
recv:
;called from the recv isr. All registers have been saved, and ds=cs.
;Upon exit, the interrupt will be acknowledged.
assume ds:code
mov es,base_addr
assume es:ub_seg
recv_3:
test epp,PAV
jne recv_4
jmp recv_2 ;yes.
recv_4:
;checking against epp is a pain actually...
xor cx,cx ;start with zero length
mov bl,current_page ;get the first page.
xor bh,bh
mov dx,bx ;remember the first page.
count_pages_1:
mov al,page_ram[bx] ;Get page descriptor
and al,RPAGE_BITS ;Only page offset bits PLEASE
inc al ;Add 1 to offset to make length
xor ah,ah
add cx,ax ;Add in current page length
mov al,page_ram[bx] ;Get page descriptor
inc bx ;go to the next page
cmp bx,NUM_RPAGES ;wrap around if necessary.
jb count_pages_2
xor bx,bx
count_pages_2:
test al,80h ;End page?
je count_pages_1 ;yes, go add this one's length.
mov current_page,bl
mov ax,dx ;multiply dx by RPAGE_LEN.
xchg ah,al
ror ax,1
mov di,ax
add di,offset receive_buf ;get the starting address.
add di,EADDR_LEN+EADDR_LEN ;skip the ethernet addreses and
; point to the packet type.
push bx
push cx
push dx
mov dl, BLUEBOOK ;assume bluebook Ethernet.
mov ax, es:[di]
xchg ah, al
cmp ax, 1500
ja BlueBookPacket
inc di ;set di to 802.2 header
inc di
mov dl, IEEE8023
BlueBookPacket:
call recv_find ;look up our type.
assume es:nothing
pop dx
pop cx
pop bx
mov ax,es ;is this pointer null?
or ax,di
je just_discard ;yes - just free the frame.
push cx
push di
push es
cmp dx,bx ;did we wrap around (first >last?)
jbe no_wrap ;no, just do one move.
;we wrapped around the buffer, so we have to move in two halves.
;move the first part.
mov ax,dx ;multiply dx by RPAGE_LEN.
xchg ah,al
ror ax,1
mov si,ax
add si,offset receive_buf ;get the starting address.
mov ax,NUM_RPAGES ;Compute the number of pages to move.
sub ax,dx
xchg ah,al ;convert to pages (*RPAGE_LEN).
ror ax,1
cmp cx,ax ;are we moving less than that?
jbe no_wrap ;yes, just move it.
sub cx,ax ;reduce the total count by this count.
push cx ;preserve the second part's count
mov cx,ax ;get the first part's count.
push ds
mov ds,base_addr
assume ds:nothing
call movemem
assume ds:code
pop ds
pop cx ;get back the count
xor dx,dx
no_wrap:
mov ax,dx ;multiply dx by 128.
xchg ah,al
ror ax,1
mov si,ax
add si,offset receive_buf
mov ds,base_addr
assume ds:nothing
call movemem
assume ds:code
pop ds
pop si
pop cx
call recv_copy
push cs
pop ds
just_discard:
mov es,base_addr
assume es:ub_seg
mov al,epp ;Any more packets left?
and al,RPAGE_BITS
cmp al,current_page
je recv_5
jmp recv_3
recv_5:
cmp al,tsa_lsb ;Clearing pav bit (SIDEEFFECT)
recv_2:
;Ack the interrupt by turning bit on and off
mov al,current_page ;Set first free page pointer
mov ffp,al
or al,NIC_IE ;set interrupt bit, was off
mov ffp,al
recv_1:
ret
public recv_exiting
recv_exiting:
;called from the recv isr after interrupts have been acknowledged.
;Only ds and ax have been saved.
assume ds:nothing
ret
;any code after this will not be kept after initialization.
end_resident label byte
public usage_msg
usage_msg db "usage: ubnic [-n] [-d] [-w] <packet_int_no> <int_no> <base_addr>",CR,LF,'$'
public copyright_msg
copyright_msg db "Packet driver for the UB PC/NIC, version ",'0'+majver,".",'0'+version,CR,LF
db '$'
int_no_name db "Interrupt number ",'$'
base_addr_name db "Memory address ",'$'
our_address db 6 dup(?) ;temporarily hold our address
extrn set_recv_isr: near
;enter with si -> argument string, di -> wword to store.
;if there is no number, don't change the number.
extrn get_number: near
;enter with si -> argument string, di -> wword to print.
extrn print_number: near
public parse_args
parse_args:
;exit with nc if all went well, cy otherwise.
mov di,offset int_no
call get_number
mov di,offset base_addr
call get_number
clc
ret
public etopen
etopen:
cli
cmp base_addr,-1 ;did they ask for auto-configure?
jne no_auto_config
mov bx,8000h ;yes, they *could* address it there.
auto_config_1:
mov es,bx
assume es:ub_seg
cmp word ptr es:eaddr_rom[0],00h + 0ddh * 256
jne auto_config_2
cmp byte ptr es:eaddr_rom[2],01h
je auto_config_3
auto_config_2:
add bx,800h ;move up by 32K
jnc auto_config_1 ;go if we didn't overflow.
sti
stc ;say that we couldn't init.
ret
auto_config_3:
mov base_addr,bx ;remember where we found it.
mov base_addr+2,0 ;and nuke the rest of the -1.
no_auto_config:
mov es,base_addr
assume es:ub_seg
mov reset,80h ;Turn on reset line to tie down NIC
mov rmode,0 ;no packets for now
mov tmode,0 ;Turn on loopback
mov al,epp ;start ffp at epp - eliminates bogus ....
and al,07fh
mov current_page,al ;... packets if we've been started before
mov ffp,al ;Make sure interrupt enable is dead
cmp al,tsa_lsb ;Clearing pav bit (SIDEEFFECT)
mov tstatus, 0fh ;Clear xmit status bits
mov tmask,0 ;No xmit intrs
mov rmask,0 ;'Packet okay' only?
mov rstatus,0ffh ;Clear receiver status bits
push ds
pop es
mov di,offset our_address
mov cx,EADDR_LEN
call get_address
mov si,offset our_address
mov cx,EADDR_LEN
call set_address
;Since first packet will get sent no matter what we do, let's make it
;something everyone (include me) will ignore
;set length to zero
mov tsa_lsb,0ffh ;Get last 8 bits
mov tsa_msb,0fh ;and top 4 bits
mov reset,0 ;Clear reset - expect packet to go
;Wait for packet to go!
etopen_1:
mov al,csr
and al,TPOK or TXDONE
cmp al,TPOK or TXDONE
jne etopen_1
mov tstatus,0fh ;clear xmiter status
mov tmode,0ah ;Clear loopbk, set parity enable
mov csr,0 ;make sure this interrupt was off first
mov csr,PAVIntEn ;Page frame available interrupt only
mov al,current_page ;Set first free page register ...
or al,NIC_IE
mov ffp,al ;... and make sure interrupts enabled
mov al,epp ;get epp again
test al,PAV
je etopen_2
cmp al,tsa_lsb ;Try clearing pav bit (SIDEEFFECT)
etopen_2:
;enable receiving now
; 1 - accept node id packets, multicasts which match first three
; bytes of node id, and broadcasts
mov rmode,1
;
; Now hook in our interrupt
;
call set_recv_isr
sti
mov al, int_no ; Get board's interrupt vector
add al, 8
cmp al, 8+8 ; Is it a slave 8259 interrupt?
jb set_int_num ; No.
add al, 70h - 8 - 8 ; Map it to the real interrupt.
set_int_num:
xor ah, ah ; Clear high byte
mov int_num, ax ; Set parameter_list int num.
;if all is okay,
mov dx,offset end_resident
clc
ret
;if we got an error,
stc
ret
public print_parameters
print_parameters:
;echo our command-line parameters
mov di,offset int_no
mov dx,offset int_no_name
call print_number
mov di,offset base_addr
mov dx,offset base_addr_name
call print_number
ret
code ends
end